/********************************************
 Trabajo Práctico de Laboratorio 3
 Autores:
 2do Cuatrimestre 2012
 Prof: Daniel Barrientos
 *************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include "headers/servidor.h"

typedef struct OnAcceptData {
	Servidor *servidor; // la informacion del servidor
	int clientFd; // el file descriptor del cliente que se conecto
} OnAcceptData;

void *handleNewConnection(void *onAcceptData){
	OnAcceptData *data = (OnAcceptData*) onAcceptData;
	onAccept(data->servidor, data->clientFd);
	pthread_exit(NULL);
}

void onAccept(Servidor* servidor, int clientFD/*, struct sockaddr* clientAddress*/); // handler del accept(..)
int actionShowJugadoresAndSelect(Servidor* servidor, int clientFD); // manda al cliente la lista de jugadores disponibles y espera la eleccion de uno
Jugador* getJugadorDisponibleByID(Servidor* servidor, int id);

Servidor* Servidor_create() {
	Servidor* servidor = malloc(sizeof(Servidor));
	servidor->jugadores = ListaJugadores_create();
	servidor->jugadoresDisponibles = ListaJugadores_create();
	return servidor;
}

void Servidor_destroy(Servidor* servidor) {
	ListaJugadores_destroy(servidor->jugadores);
	ListaJugadores_destroy(servidor->jugadoresDisponibles);
	free(servidor);
}

void startupServer(Servidor* servidor) {
	int socketSize;     // hace falta para pasarlo al "accept()"
	int logFD;      // FD para loguear lo q llega del cliente

	int clientFD;      // aca va el FD para el socket del cliente que se conecta
	struct sockaddr_in* clientAddress; // aca se guardan los datos de los clientes que se conectarn al server

	// El socket q escucha
	int listenerFD = socket(AF_INET, SOCK_STREAM, 0);

	// el address del server
	struct sockaddr_in serverAddress;  // aca se guardan los datos del servidor
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = INADDR_ANY;
	serverAddress.sin_port = htons(SERVER_PORT);

	// bindeo el socket con el port del serverAddress
	if (bind(listenerFD, (struct sockaddr*) &serverAddress,
			sizeof(struct sockaddr_in)) < 0) {
		puts("Error creando el socket");
		exit(EXIT_FAILURE);
	}

	puts("Servidor iniciado y listo para recibir conexiones...");
	// pongo a escuchar
	listen(listenerFD, 2);

	// loop infinito para escuchar y aceptar conexiones entrantes
	while (1) {
		socketSize = sizeof(struct sockaddr_in);
		clientAddress = malloc(sizeof(struct sockaddr_in));
		// me quedo esperando que algun cliente entre
		clientFD = accept(listenerFD, (struct sockaddr*) clientAddress,
				&socketSize);
		OnAcceptData data;
		data.servidor = servidor;
		data.clientFd = clientFD;

		onAccept(servidor, clientFD);
	}
	// TODO: capturar SIGNAL de Ctrl+C para liberar todos los recursos usados
}

void onAccept(Servidor* servidor, int clientFD) {
	char buffer[512];
	int n;
	int id;
	int selectedOpt;
	Jugador* jugador;
	Jugador* oponente;
	Partida* partida;

	// imprimo en el server (como LOG) quien entro
	printf("Nueva conexion desde ...\n");

	// leo nombre del jugador
	memset(buffer, '\0', 512);
	n = read(clientFD, buffer, 512);

	write(1, "Se conecto: ", 13);
	write(1, buffer, strlen(buffer));
	write(1, "\n", 1);

	// creo el jugador
	jugador = Jugador_crear(buffer, clientFD);

	// Agrego el jugador a la lista de disponibles
	Servidor_addJugadorDisponible(servidor, jugador);

	// le muestro al jugador todos los jugadores disponibles
	selectedOpt = actionShowJugadoresAndSelect(servidor, clientFD);
	puts("salioooooooooo----------");

	if (selectedOpt != 0) {
		// eligio un oponente
		oponente = getJugadorDisponibleByID(servidor, selectedOpt);
		partida = Partida_crear(jugador, oponente); // creo la partida y la inicializo
		Partida_iniciarAsincronicamente(partida);

		// cuando la partida termina-> desconectar a los clientes
/*
		close(Jugador_getSocket(jugador));
		close(Jugador_getSocket(oponente));

		// libero recursos
		free(partida);
		free(jugador);
		free(oponente);
*/

	} else {
		// no eligio oponente -> se queda esperando

		// TODO : mostrar la lista actualizada cada 20 segundos y quedarse esperando respuesta (usar un timer o algo asi)

	}

}

int actionShowJugadoresAndSelect(Servidor* servidor, int clientFD) {
	char buffer[512];
	int i;
	int n;
	int selectedOpt;
	Jugador* jugador;
	char id_str[16];
	NodoLista* nodo;

	memset(buffer, '\0', 512);
	strcat(buffer, "Lista de Jugadores\n------------------\n");
	strcat(buffer, "0 - (Esperar)\n");

	nodo = ListaJugadores_getFirst(servidor->jugadoresDisponibles);
	while (nodo != NULL ) {
		jugador = nodo->item;
		memset(id_str, '\0', 16);
		sprintf(id_str, "%i\0", Jugador_getId(jugador));
		strcat(buffer, id_str);
		strcat(buffer, " - ");
		strcat(buffer, Jugador_getNombre(jugador));
		strcat(buffer, "\n");

		nodo = ListaJugadores_getNext(servidor->jugadoresDisponibles, nodo);
	}

	write(clientFD, buffer, strlen(buffer));    // lo mando al cliente

	// leo la opcion elegida por el cliente
	memset(buffer, '\0', 512);
	n = read(clientFD, buffer, 512);
	selectedOpt = atoi(buffer);   // la opcion elegida

	//write(1, buffer, sizeof(buffer));
	printf("Selected: %i\n", selectedOpt);
	return selectedOpt;
}

void Servidor_addJugadorDisponible(Servidor* servidor, Jugador* jugador) {
	ListaJugadores_add(servidor->jugadoresDisponibles, jugador);
}

Jugador* getJugadorDisponibleByID(Servidor* servidor, int id) {
	return ListaJugadores_get(servidor->jugadoresDisponibles, id);
}

// Crea la partida entre los jugadores.
struct Partida* crearPartida(struct Servidor servidor, struct Jugador* jugador1,
		struct Jugador* jugador2) {
	struct Partida* partida = malloc(sizeof(Partida));
	partida->jugador1 = jugador1;
	partida->jugador2 = jugador2;

	return partida;
}


int main(int argc, char* argv[]) {
	Servidor* servidor = Servidor_create();
	Jugador* j = Jugador_crear("pepe", (int) NULL );
	ListaJugadores_add(servidor->jugadoresDisponibles, j);
	startupServer(servidor);
	Servidor_destroy(servidor);
	return 0;
}


